/*
 * /linux-3.0.8/drivers/spi/gt210_nrf2401.c
 */
 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/compat.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/list.h>

#include <mach/irqs.h>
#include <mach/regs-gpio.h>

#include <asm/uaccess.h>

#include "gt210_nrf2401.h"
#include "nrf2401_test.h"

static DECLARE_BITMAP(minors, N_SPI_MINORS);
static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);

static struct class *gt210_nrf2401_class = NULL;

static const struct file_operations gt210_nrf2401_fops = {
	.owner =	THIS_MODULE,
	.llseek 		= no_llseek,
	.unlocked_ioctl = gt210_nrf2401_ioctl,
	.compat_ioctl    = gt210_nrf2401_ioctl,
	.open 		= gt210_nrf2401_open,
	.release 	=	 gt210_nrf2401_release,
};



/***** write and read reg ****/
static int gt210_nrf2401_write_reg (struct gt210_nrf2401_info *info,
								unsigned char reg, unsigned char value)
{
	int ret;
	unsigned char tx_buf[4]={0};
	unsigned char rx_buf[4]={0};
	tx_buf[0] = reg ;
	tx_buf[1] = value;
	//ret = spi_write_then_read(info->spi_dev, tx_buf, 2, rx_buf, 1);
	ret = spi_write(info->spi_dev, tx_buf, 2);
	if (ret)
		dprintk("failed to write reg: "
			"addr=0x%02x, status=0x%02x, value=0x%02x, ret = %d\n", 
			reg, rx_buf[0], tx_buf[1],ret);
	return ret;
}
static int gt210_nrf2401_read_reg (struct gt210_nrf2401_info *info,
								unsigned char reg,unsigned char *value)
{
	unsigned char tx_buf[4];
	unsigned char rx_buf[4]={0xff};
	int ret;
	tx_buf[0] = reg;
	//tx_buf[1] = reg;
	ret = spi_write_then_read(info->spi_dev, tx_buf, 1, rx_buf, 2);
	if (ret)
		dprintk("failed to read reg: "
			"addr=0x%02x, status=0x%02x, value=0x%02x, ret = %d\n", 
			reg, rx_buf[0], rx_buf[1],ret);
	else
		*value = rx_buf[1];
	return ret;
}
static int gt210_nrf2401_read_status(struct gt210_nrf2401_info *info,
								unsigned char reg,unsigned char *value)
{
	unsigned char tx_buf[4];
	unsigned char rx_buf[4];
	int ret;
	tx_buf[0] = reg ;
	ret = spi_write_then_read(info->spi_dev, tx_buf, 1, rx_buf, 2);
	if (ret)
		dprintk("failed to read status: "
			"addr=0x%02x, status=0x%02x, ret = %d\n", 
			reg, rx_buf[0],ret);
	else
		*value = rx_buf[0];
	return ret;
}
static int gt210_nrf2401_write_buf(struct gt210_nrf2401_info *info,
								unsigned char reg, unsigned char *buf, 
								unsigned char len)
{
	int ret;
	unsigned char tx_buf[NRF2401_PIPE_LEN+ 4];
	unsigned char rx_buf[4];
	tx_buf[0] = reg ;
	memcpy(&tx_buf[1], buf, len);
	//ret = spi_write_then_read(info->spi_dev, tx_buf, len + 1, rx_buf, 1);
	ret = spi_write(info->spi_dev, tx_buf, len+1);
	if (ret)
		dprintk("failed to write buf: "
			"addr=0x%02x, status=0x%02x, value=0x%02x, ret = %d\n", 
			reg, rx_buf[0], *buf,ret);
	return ret;
}  
/***** the two read buf function is both good on here *****/
#if 0
static int gt210_nrf2401_read_buf(struct gt210_nrf2401_info *info,
								unsigned char reg, unsigned char *buf, 
								unsigned char len)
{
	int i = 0;
	int ret = 0;
	for(i = 0; i < len; i++, buf++){
		ret = gt210_nrf2401_read_reg(info, reg, buf);
	}
	return ret;
} 
#else
static int gt210_nrf2401_read_buf(struct gt210_nrf2401_info *info,
								unsigned char reg, unsigned char *buf, 
								unsigned char len)
{
	u8 tx_buf[NRF2401_PIPE_LEN + 1] = {0};
	u8 rx_buf[NRF2401_PIPE_LEN + 1] = {0};
	int ret;
	tx_buf[0] = reg ;
	ret = spi_write_then_read(info->spi_dev, tx_buf, 1, rx_buf, len + 1);
	if (ret)
		dprintk("failed to read buf: "
			"addr=0x%02x, status=0x%02x,value=0x%02x, ret = %d\n", 
			reg, rx_buf[0], rx_buf[1], ret);
	memcpy(buf, &rx_buf[1], len); /* skip the first status byte */
	return ret;
} 
#endif


/***** chip set function *****/
static int gt210_nrf2401_init_chip(struct gt210_nrf2401_info *info)
{
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret = 0;
	unsigned char value=0xff;
	(info->pdata->clear_cepin)(pdata);		
	gt210_nrf2401_write_reg(info, WRITE_REG1 | EN_AA, 
						info->pdata->en_aa); 
	//msleep(10);
	dprintk("write en aa reg: addr=0x%02x, value=0x%02x\n", EN_AA, info->pdata->en_aa); 
	gt210_nrf2401_read_reg(info,READ_REG1 | EN_AA, &value);
	dprintk("read en aa reg: addr=0x%02x, value=0x%02x\n", EN_AA, value);
			
  	gt210_nrf2401_write_reg(info, WRITE_REG1 | EN_RXADDR, 
						info->pdata->en_rxaddr); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RF_CH, 
						info->pdata->rf_ch); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | SETUP_AW, 
						info->pdata->setup_aw); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | SETUP_RETR, 
						info->pdata->setup_retr); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RF_CH, 
						info->pdata->rf_ch); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RF_SETUP, 
						info->pdata->rf_setup); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P0, 
						info->pdata->rx_pw_p0); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P1, 
						info->pdata->rx_pw_p1); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P2, 
						info->pdata->rx_pw_p2); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P3, 
						info->pdata->rx_pw_p3); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P4, 
						info->pdata->rx_pw_p4); 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | RX_PW_P5, 
						info->pdata->rx_pw_p5); 
  	gt210_nrf2401_write_reg(info, WRITE_REG1 | CONFIG, 0x0f); 
  	
  	dprintk("write config reg: addr=0x%02x, value=0x%02x\n", CONFIG, 0x0f); 
	gt210_nrf2401_read_reg(info,READ_REG1 | CONFIG, &value);
	dprintk("read config reg: addr=0x%02x, value=0x%02x\n", CONFIG, value);
	(info->pdata->set_cepin)(pdata);	
	
	return ret;
}
static int gt210_nrf2401_deinit_chip(struct gt210_nrf2401_info *info)
{
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret;
	(info->pdata->clear_cepin)(pdata);	
	gt210_nrf2401_write_reg(info, WRITE_REG1 | CONFIG, 0x78); 
	(info->pdata->set_cepin)(pdata);	
	
	return ret;
}

static int gt210_nrf2401_init_op(struct gt210_nrf2401_info *info)
{	dprintk("gt210_nrf2401_init_op()\n");
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret;
	ret = request_irq(info->irq, gt210_nrf2401_irq, 
		info->pdata->irq_flags, info->spi_dev->modalias, info);
	if(ret < 0){
		dev_err(&info->spi_dev->dev,"failed to request irq: %d, error=%d", 
			info->irq, ret);
		ret = -EBUSY;
		return ret;
	}
	if(pdata->init_pin)
		pdata->init_pin(pdata);
	gt210_nrf2401_init_chip(info);
	
	return ret;
}
static int gt210_nrf2401_deinit_op(struct gt210_nrf2401_info *info)
{	dprintk("gt210_nrf2401_deinit_op()\n");
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret;
	free_irq(info->irq, info);
	gt210_nrf2401_deinit_chip(info);
	if(pdata->exit_pin)
		pdata->exit_pin(pdata);
	return ret;
}


static int gt210_nrf2401_set_tx_addr(struct gt210_nrf2401_pipe *pipe)
{	
	struct gt210_nrf2401_info *info = pipe->info;
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret;
	(info->pdata->clear_cepin)(pdata);	 
	ret = gt210_nrf2401_write_buf(info, WRITE_REG1 | TX_ADDR, 
						pipe->addr, NRF2401_ADDR_LEN); 
	(info->pdata->set_cepin)(pdata);
	return ret;
}
static int gt210_nrf2401_set_rx_addr(struct gt210_nrf2401_pipe *pipe)
{	
	struct gt210_nrf2401_info *info = pipe->info;
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int ret;
	(info->pdata->clear_cepin)(pdata);	 
	ret = gt210_nrf2401_write_buf(info, 
						WRITE_REG1 | RX_ADDR_P0 + pipe->addr_num, 
						pipe->addr, 
						(pipe->addr_num == NRF2401_ADDR_0 || pipe->addr_num == NRF2401_ADDR_1) ?
						NRF2401_ADDR_LEN :
						NRF2401_ADDR_LEN - 4 ); 
	(info->pdata->set_cepin)(pdata);
	return ret;
}
static int gt210_nrf2401_send_buf(struct gt210_nrf2401_pipe *pipe,
								struct gt210_nrf2401_pbuf *pbuf)
{	
	struct gt210_nrf2401_info *info = pipe->info;
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int i = 0;
	int ret = 0;
	(info->pdata->clear_cepin)(pdata);		 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | STATUS,0xff);
	gt210_nrf2401_write_reg(info, WRITE_REG1 | 0xe1,0);
	gt210_nrf2401_write_reg(info, WRITE_REG1 | 0xe2,0);
	gt210_nrf2401_write_reg(info, WRITE_REG1 |  CONFIG, 0x0e);	
	//msleep(10); 
  	ret = gt210_nrf2401_write_buf(info, WR_TX_PLOAD, 
						pbuf->pipe_buf, NRF2401_PIPE_LEN);  
	(info->pdata->set_cepin)(pdata);	
	return ret;
}
static int gt210_nrf2401_recv_buf(struct gt210_nrf2401_pipe *pipe)
{	
	struct gt210_nrf2401_info *info = pipe->info;
	struct gt210_nrf2401_platdata *pdata = info->pdata;
	int i = 0;
	int ret = 0;
	(info->pdata->clear_cepin)(pdata);		 
	gt210_nrf2401_write_reg(info, WRITE_REG1 | STATUS,0xff);
	gt210_nrf2401_write_reg(info, WRITE_REG1 | 0xe1,0);
	gt210_nrf2401_write_reg(info, WRITE_REG1 | 0xe2,0);
	gt210_nrf2401_write_reg(info, WRITE_REG1 | CONFIG, 0x0f); 					
	//msleep(10); 
	(info->pdata->set_cepin)(pdata);	
	return ret;
}



/***** pipe buf help function *****/
static struct gt210_nrf2401_pbuf *gt210_nrf2401_alloc_pbuf(struct nrf2401_ioc_transfer *xfer)
{
	struct gt210_nrf2401_pbuf *pbuf = NULL;
	pbuf = kzalloc(sizeof(struct gt210_nrf2401_pbuf), GFP_KERNEL);
	if(pbuf != NULL){
		INIT_LIST_HEAD(&pbuf->list);
		if(xfer != NULL)
			memcpy(pbuf->pipe_buf, xfer->txrx_buf, NRF2401_PIPE_LEN);
	}
	return pbuf;
}
static void gt210_nrf2401_free_pbuf(struct gt210_nrf2401_pbuf *pbuf)
{
	if(pbuf)
		kfree(pbuf);
}


/***** help function *****/
static void gt210_nrf2401_timer(unsigned long data)
{	dprintk("gt210_nrf2401_timer()\n");
	struct gt210_nrf2401_pipe *pipe = (struct gt210_nrf2401_pipe *)data;
	//pipe->error = -ENXIO;
	pipe->error = 0;
	complete(&pipe->done);
}

static void gt210_nrf2401_tx_work(struct work_struct *work)
{	dprintk("gt210_nrf2401_tx_work()\n");
	struct gt210_nrf2401_pipe *pipe =
		container_of(work, struct gt210_nrf2401_pipe, work);
	dev_info(&pipe->info->spi_dev->dev, "\n");
	struct gt210_nrf2401_pbuf *pbuf, *tmpbuf;
	while(pipe->has_packets > 0){
		spin_lock(&pipe->lock);
		if(pipe->pbuf == NULL && pipe->has_packets > 0){
			pipe->pbuf  = list_entry(pipe->head.next, struct gt210_nrf2401_pbuf, list);
			//dev_info(&pipe->info->spi_dev->dev, "if(): pipe->pbuf = 0x%08x,\n", pipe->pbuf);
			spin_unlock(&pipe->lock);
			gt210_nrf2401_set_rx_addr(pipe);
			gt210_nrf2401_send_buf(pipe, pipe->pbuf);
			msleep(10);
			break;
		}else{
			//dev_info(&pipe->info->spi_dev->dev, "else(): pipe->pbuf = 0x%08x,\n", pipe->pbuf);
			tmpbuf = pipe->pbuf;
			pipe->pbuf  = NULL;
			list_del(&tmpbuf ->list);
			pipe->has_packets--;
			spin_unlock(&pipe->lock);
			
			gt210_nrf2401_free_pbuf(tmpbuf);
			if(pipe->xfer && tmpbuf == pipe->xbuf){
				complete(&pipe->done);
			}
		}
	}
}
static void gt210_nrf2401_rx_work(struct work_struct *work)
{	dprintk("gt210_nrf2401_rx_work()\n");
	struct gt210_nrf2401_pipe *pipe =
		container_of(work, struct gt210_nrf2401_pipe, work);

	struct gt210_nrf2401_pbuf *pbuf;
	if(pipe->xfer && pipe->xfer->rw_mode == NRF2401_IOC_RW_BLOCK){
		spin_lock(&pipe->lock);
		if(pipe->has_packets > 0){
			//dev_info(&pipe->info->spi_dev->dev, "list_del(): pipe->pbuf = 0x%08x,\n", pipe->pbuf);
			pbuf = list_entry(pipe->head.next, struct gt210_nrf2401_pbuf, list);
			list_del(pipe->head.next);
			pipe->has_packets--;
			spin_unlock(&pipe->lock);
			memcpy(pipe->xfer->txrx_buf, pbuf->pipe_buf, 
					NRF2401_PIPE_LEN);
			gt210_nrf2401_free_pbuf(pbuf);
			complete(&pipe->done);
		}else
			spin_unlock(&pipe->lock);
	}
}



/***** ioctl ops pass function *****/
static int gt210_nrf2401_set_chip(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{
	int ret = 0;
	mutex_lock(&info->buf_lock);
	if(info->tx_pipe ||
		info->rx_pipe[0] ||
		info->rx_pipe[1] ||
		info->rx_pipe[2] ||
		info->rx_pipe[3] ||
		info->rx_pipe[4] ||
		info->rx_pipe[5]){
		ret = -EBUSY;
		goto exit_1;
	}
	info->pdata->en_aa = xfer->txrx_buf[0];
	info->pdata->en_rxaddr = xfer->txrx_buf[1];
	info->pdata->setup_aw = xfer->txrx_buf[2];
	info->pdata->setup_retr = xfer->txrx_buf[3];
	info->pdata->rf_ch = xfer->txrx_buf[4];
	info->pdata->rf_setup = xfer->txrx_buf[5];
	info->pdata->rx_pw_p0 = xfer->txrx_buf[6];
	info->pdata->rx_pw_p1 = xfer->txrx_buf[7];
	info->pdata->rx_pw_p2 = xfer->txrx_buf[8];
	info->pdata->rx_pw_p3 = xfer->txrx_buf[9];
	info->pdata->rx_pw_p4 = xfer->txrx_buf[10];
	info->pdata->rx_pw_p5 = xfer->txrx_buf[11];
	ret = gt210_nrf2401_init_chip(info);
exit_1:
	mutex_unlock(&info->buf_lock);
	return ret;
}
static int gt210_nrf2401_get_chip(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{
	int ret = 0;
	xfer->txrx_buf[0] = info->pdata->en_aa;
	xfer->txrx_buf[1] = info->pdata->en_rxaddr;
	xfer->txrx_buf[2] = info->pdata->setup_aw;
	xfer->txrx_buf[3] = info->pdata->setup_retr;
	xfer->txrx_buf[4] = info->pdata->rf_ch;
	xfer->txrx_buf[5] = info->pdata->rf_setup;
	xfer->txrx_buf[6] = info->pdata->rx_pw_p0;
	xfer->txrx_buf[7] = info->pdata->rx_pw_p1;
	xfer->txrx_buf[8] = info->pdata->rx_pw_p2;
	xfer->txrx_buf[9] = info->pdata->rx_pw_p3;
	xfer->txrx_buf[10] = info->pdata->rx_pw_p4;
	xfer->txrx_buf[11] = info->pdata->rx_pw_p5;	
	return ret;
}
static int gt210_nrf2401_set_tx_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{	dprintk("gt210_nrf2401_set_tx_pipe()\n");
	//dev_info(&info->spi_dev->dev, "pipe = %d : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", 
	//	xfer->addr_num, xfer->txrx_buf[4], xfer->txrx_buf[3],
	//	xfer->txrx_buf[2], xfer->txrx_buf[1], xfer->txrx_buf[0]);	
	struct gt210_nrf2401_pipe *pipe;
	int ret;
	mutex_lock(&info->buf_lock);
	pipe = info->tx_pipe;
	if(pipe){
		 if(pipe->has_packets){
			ret = -EBUSY;
		}else{
			memcpy(pipe->addr, xfer->txrx_buf, NRF2401_ADDR_LEN);
			ret = gt210_nrf2401_set_tx_addr(pipe);
		}
		goto exit_1;
	}
	pipe = kzalloc(sizeof(struct gt210_nrf2401_pipe), GFP_KERNEL);
	if(pipe == NULL){
		dev_err(&info->spi_dev->dev, "failed to allocate struct gt210_nrf2401_pipe{}\n");
		ret =  -ENOMEM;
		goto exit_1;
	}
	info->tx_pipe = pipe;
	
	pipe->mode = NRF2401_PIPE_TX_MODE;
	pipe->addr_num = NRF2401_ADDR_0;
	//pipe->delay_ms = xfer->delay_ms;
	pipe->info = info;
	pipe->callback = gt210_nrf2401_tx_callback;
	INIT_LIST_HEAD(&pipe->head);
	spin_lock_init(&pipe->lock);
	mutex_init(&pipe->mutex);
	init_completion(&pipe->done);
	INIT_WORK(&pipe->work, gt210_nrf2401_tx_work);
	setup_timer(&pipe->timer, gt210_nrf2401_timer, (unsigned long)pipe);
	memcpy(pipe->addr, xfer->txrx_buf, NRF2401_ADDR_LEN);
	ret = gt210_nrf2401_set_tx_addr(pipe);
exit_1:
	mutex_unlock(&info->buf_lock);
	
	return ret;
}
static int gt210_nrf2401_get_tx_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{
	struct gt210_nrf2401_pipe *pipe;
	int ret = 0;
	pipe = info->tx_pipe;
	if(pipe){
		memcpy(xfer->txrx_buf, pipe->addr, NRF2401_ADDR_LEN);
	}else{
		ret = -ENOENT;
	}
	return ret;
}

static int gt210_nrf2401_set_rx_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{	dprintk("gt210_nrf2401_set_rx_pipe()\n");
	//dev_info(&info->spi_dev->dev, "pipe = %d : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", 
	//	xfer->addr_num, xfer->txrx_buf[4], xfer->txrx_buf[3],
	//	xfer->txrx_buf[2], xfer->txrx_buf[1], xfer->txrx_buf[0]);	
	struct gt210_nrf2401_pipe *pipe;
	int ret;
	int i=0;
	mutex_lock(&info->buf_lock);
	if(xfer->addr_num < NRF2401_ADDR_0 && 
		xfer->addr_num > NRF2401_ADDR_5){
		ret = -EINVAL;
		goto exit_1;
	}
	pipe = info->rx_pipe[xfer->addr_num];
	if(pipe){
		 if(pipe->has_packets || pipe->xfer){
			ret = -EBUSY;
		}else{
			memcpy(pipe->addr, xfer->txrx_buf, NRF2401_ADDR_LEN);
			ret = gt210_nrf2401_set_rx_addr(pipe);
		}
		goto exit_1;
	}
	pipe = kzalloc(sizeof(struct gt210_nrf2401_pipe), GFP_KERNEL);
	if(pipe == NULL){
		dev_err(&info->spi_dev->dev, "failed to allocate struct gt210_nrf2401_pipe{}\n");
		ret = -ENOMEM;
		goto exit_1;
	}
	pipe->mode = NRF2401_PIPE_RX_MODE;
	pipe->addr_num = xfer->addr_num;
	//pipe->delay_ms = xfer->delay_ms;
	pipe->info = info;
	pipe->callback = gt210_nrf2401_rx_callback;
	INIT_LIST_HEAD(&pipe->head);
	
	spin_lock_init(&pipe->lock);
	mutex_init(&pipe->mutex);
	init_completion(&pipe->done);
	INIT_WORK(&pipe->work, gt210_nrf2401_rx_work);
	setup_timer(&pipe->timer, gt210_nrf2401_timer, (unsigned long)pipe);
	memcpy(pipe->addr, xfer->txrx_buf, NRF2401_ADDR_LEN);
	info->rx_pipe[pipe->addr_num] = pipe ;
	ret = gt210_nrf2401_set_rx_addr(pipe);
	ret = gt210_nrf2401_recv_buf(pipe);
	
exit_1:
	mutex_unlock(&info->buf_lock);
	return ret;
}
static int gt210_nrf2401_get_rx_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{
	struct gt210_nrf2401_pipe *pipe;
	int ret;
	pipe = info->rx_pipe[xfer->addr_num];
	if(pipe){
		memcpy(xfer->txrx_buf, pipe->addr, NRF2401_ADDR_LEN);
	}else{
		ret = -ENOENT;
	}
	return ret;
}

static int gt210_nrf2401_write_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{	dprintk("gt210_nrf2401_write_pipe(pipe addr = 0x%08x)\n", info->tx_pipe);
	struct gt210_nrf2401_pipe *pipe;
	struct gt210_nrf2401_pbuf *pbuf;
	int ret = 0;
	mutex_lock(&info->buf_lock);
	pipe = info->tx_pipe;
	if(pipe == NULL){
		mutex_unlock(&info->buf_lock);
		ret = -ENOENT;
		return ret;
	}else
		mutex_unlock(&info->buf_lock);
	
	mutex_lock(&pipe->mutex);
	if(pipe->xfer && xfer->rw_mode == NRF2401_IOC_RW_BLOCK){
		ret = -EBUSY;
		goto exit_1;
	}
	pipe->xfer = xfer;
	pbuf = gt210_nrf2401_alloc_pbuf(xfer);
	if(pbuf == NULL){
		dev_err(&info->spi_dev->dev, "failed to allocate struct gt210_nrf2401_pbuf{}\n");
		ret = -ENOMEM;
		goto exit_1;
	}
	pipe->xbuf = pbuf;
	spin_lock(&pipe->lock);
	list_add_tail(&pbuf->list, &pipe->head);
	pipe->has_packets++;
	if(pipe->has_packets == 1){
		spin_unlock(&pipe->lock);
		schedule_work(&pipe->work);
	}else
		spin_unlock(&pipe->lock);
		
	if(xfer->rw_mode == NRF2401_IOC_RW_NOBLOCK){
		ret = 0;
	}else if(xfer->rw_mode == NRF2401_IOC_RW_BLOCK){
		if (xfer->delay_ms)
			mod_timer(&pipe->timer, jiffies + msecs_to_jiffies(xfer->delay_ms));
		wait_for_completion(&pipe->done);
		ret = pipe->error;
	}
	
	
exit_1:
	pipe->xfer = NULL;
	pipe->xbuf = NULL;
	mutex_unlock(&pipe->mutex);
	return ret;
}
static int gt210_nrf2401_read_pipe(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{	dprintk("gt210_nrf2401_read_pipe()\n");
	struct gt210_nrf2401_pipe *pipe = info->rx_pipe[xfer->addr_num];
	struct gt210_nrf2401_pbuf *pbuf ;
	int ret = 0;
	int i = 0;
	
	mutex_lock(&info->buf_lock);
	pipe = info->rx_pipe[xfer->addr_num];
	if(pipe == NULL){
		mutex_unlock(&info->buf_lock);
		ret = -ENOENT;
		return ret;
	}else
		mutex_unlock(&info->buf_lock);
	
	mutex_lock(&pipe->mutex);
	if(pipe->xfer && xfer->rw_mode == NRF2401_IOC_RW_BLOCK){
		ret = -EBUSY;
		goto exit_1;
	}
	pipe->xfer = xfer;
	if(xfer->rw_mode == NRF2401_IOC_RW_NOBLOCK){
		spin_lock(&pipe->lock);
		if(pipe->has_packets > 0){
			pbuf = list_entry(pipe->head.next, struct gt210_nrf2401_pbuf, list);
			list_del(pipe->head.next);
			pipe->has_packets--;
			spin_unlock(&pipe->lock);
			memcpy(pipe->xfer->txrx_buf, pbuf->pipe_buf, 
					NRF2401_PIPE_LEN);	
			gt210_nrf2401_free_pbuf(pbuf);
			ret = 0;
		}else{
			spin_unlock(&pipe->lock);
			ret = -ENOENT;	
		}
	}else if(xfer->rw_mode == NRF2401_IOC_RW_BLOCK){
		if (xfer->delay_ms)
			mod_timer(&pipe->timer,jiffies + msecs_to_jiffies(xfer->delay_ms));
		schedule_work(&pipe->work);
		wait_for_completion(&pipe->done);
		ret = pipe->error;
	}
	
exit_1:	
	pipe->xfer = NULL;
	mutex_unlock(&pipe->mutex);
	
	return ret;
}

static int gt210_nrf2401_pass_op(struct gt210_nrf2401_info *info, 
								struct nrf2401_ioc_transfer *xfer)
{	dprintk("gt210_nrf2401_pass_op()\n");
	int	ret = 0;
	
	switch(xfer->ioc_type){
	case NRF2401_IOC_NONE_0:
		break;
	case NRF2401_IOC_SET_CHIP:
		ret = gt210_nrf2401_set_chip(info, xfer);
		break;
	case NRF2401_IOC_GET_CHIP:
		ret = gt210_nrf2401_get_chip(info, xfer);	
		break;
		
	case NRF2401_IOC_SET_RX_PIPE:
		ret = gt210_nrf2401_set_rx_pipe(info, xfer);	
		break;
	case NRF2401_IOC_GET_RX_PIPE:
		ret = gt210_nrf2401_get_rx_pipe(info, xfer);	
		break;
		
	case NRF2401_IOC_SET_TX_PIPE:
		ret = gt210_nrf2401_set_tx_pipe(info, xfer);	
		break;
	case NRF2401_IOC_GET_TX_PIPE:	
		ret = gt210_nrf2401_get_tx_pipe(info, xfer);	
		break;

	case NRF2401_IOC_WRITE_PIPE:
		ret = gt210_nrf2401_write_pipe(info, xfer);
		break;
	case NRF2401_IOC_READ_PIPE:
		ret = gt210_nrf2401_read_pipe(info, xfer);
		break;
		
	default:
		break;
	}
	return ret;
}

static long gt210_nrf2401_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{	dprintk("gt210_nrf2401_ioctl()\n");
	struct gt210_nrf2401_info *info;
	struct spi_device	*spi_dev;
	struct nrf2401_ioc_transfer	*xfer;
	unsigned		n_transfer;
	u32	tmp = 0, offset = 0, len = 0;
	int	err = 0, ret = 0;
	
	if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC){
		dprintk("failed to get good magic code: \n", _IOC_TYPE(cmd));
		return -ENOTTY;
	}
	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE,
				(void __user *)arg, _IOC_SIZE(cmd));
	if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
		err = !access_ok(VERIFY_READ,
				(void __user *)arg, _IOC_SIZE(cmd));
	if (err){
		dprintk("failed to get read or write addr permission: \n", err);
		return -EFAULT;
	}
	info = filp->private_data;
	spin_lock_irq(&info->spi_lock);
	spi_dev = spi_dev_get(info->spi_dev);
	spin_unlock_irq(&info->spi_lock);
	if (spi_dev == NULL){
		dprintk("failed to get alive spi device : \n");
		return -ESHUTDOWN;
	}
	//mutex_lock(&info->buf_lock);
	switch (cmd) {
	default:
		if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
				|| _IOC_DIR(cmd) != _IOC_WRITE) {
			dprintk("failed to get used massage : \n");
			ret = -ENOTTY;
			break;
		}
		tmp = _IOC_SIZE(cmd);
		if ((tmp % sizeof(struct nrf2401_ioc_transfer)) != 0) {
			dprintk("failed to get aligned massage : \n");
			ret = -EINVAL;
			break;
		}
		n_transfer = tmp / sizeof(struct nrf2401_ioc_transfer);
		if (n_transfer == 0 || n_transfer > 1){
			dprintk("failed to support more massage : \n");
			ret = -EINVAL;
			break;
		}
		xfer = kmalloc(tmp, GFP_KERNEL);
		if (!xfer) {
			dprintk("failed to allocate struct nrf2401_ioc_transfer{}: \n");
			ret = -ENOMEM;
			break;
		}
		if (__copy_from_user(xfer, (void __user *)arg, tmp)) {
			dprintk("failed to copy k space from u space: \n");
			kfree(xfer);
			ret = -EFAULT;
			break;
		}
		ret = gt210_nrf2401_pass_op(info, xfer);
		if(ret){
			dprintk("failed to pass operation: ret = %d\n", ret);
			ret = -EFAULT;
		}else{
			switch(xfer->ioc_type){
			case NRF2401_IOC_GET_CHIP:
			case NRF2401_IOC_GET_RX_PIPE:
			case NRF2401_IOC_GET_TX_PIPE:
			case NRF2401_IOC_READ_PIPE:
				//len = NRF2401_PIPE_LEN + NRF2401_PIPE_PLUS;
				//offset = (tmp - len);	
				len = tmp;
				offset = 0;	
				if(__copy_to_user((u8 __user *)(arg) + offset, 
					((u8 *)xfer) + offset, 
					len )){
					dprintk("failed to copy k space to u space: \n");
					ret = -EFAULT;
				}
				break;
			default :
				break;	
			}
			;;;
		}
		kfree(xfer);
		break;

	/***** spi inferface mode set *****/
	case SPI_IOC_RD_MODE:
		ret = __put_user(spi_dev->mode & SPI_MODE_MASK,(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_LSB_FIRST:
		ret = __put_user((spi_dev->mode & SPI_LSB_FIRST) ?  1 : 0,(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_BITS_PER_WORD:
		ret = __put_user(spi_dev->bits_per_word, (__u8 __user *)arg);
		break;
	case SPI_IOC_RD_MAX_SPEED_HZ:
		ret = __put_user(spi_dev->max_speed_hz, (__u32 __user *)arg);
		break;
	case SPI_IOC_WR_MODE:
		ret = __get_user(tmp, (u8 __user *)arg);
		if (ret == 0) {
			u8	save = spi_dev->mode;
			if (tmp & ~SPI_MODE_MASK) {
				ret = -EINVAL;
				break;
			}
			tmp |= spi_dev->mode & ~SPI_MODE_MASK;
			spi_dev->mode = (u8)tmp;
			ret = spi_setup(spi_dev);
			if (ret < 0) 
				spi_dev->mode = save;
			else 
				dev_dbg(&spi_dev->dev, "spi mode %02x\n", tmp);
		}
		break;
	case SPI_IOC_WR_LSB_FIRST:
		ret = __get_user(tmp, (__u8 __user *)arg);
		if (ret == 0) {
			u8	save = spi_dev->mode;
			if (tmp)
				spi_dev->mode |= SPI_LSB_FIRST;
			else 
				spi_dev->mode &= ~SPI_LSB_FIRST;
			ret = spi_setup(spi_dev);
			if (ret < 0) 
				spi_dev->mode = save;
			else 
				dev_dbg(&spi_dev->dev, "%csb first\n",tmp ? 'l' : 'm');
		}
		break;
	case SPI_IOC_WR_BITS_PER_WORD:
		ret = __get_user(tmp, (__u8 __user *)arg);
		if (ret == 0) {
			u8	save = spi_dev->bits_per_word;
			spi_dev->bits_per_word = tmp;
			ret = spi_setup(spi_dev);
			if (ret < 0) 
				spi_dev->bits_per_word = save;
			else 
				dev_dbg(&spi_dev->dev, "%d bits per word\n", tmp);
		}
		break;
	case SPI_IOC_WR_MAX_SPEED_HZ:
		
		ret = __get_user(tmp, (__u32 __user *)arg);
		if (ret == 0) {
			u32	save = spi_dev->max_speed_hz;
			spi_dev->max_speed_hz = tmp;
			ret = spi_setup(spi_dev);
			if (ret < 0) 
				spi_dev->max_speed_hz = save;
			else 
				dev_dbg(&spi_dev->dev, "%d Hz (max)\n", tmp);
		}
		break;
	}
	//mutex_unlock(&info->buf_lock);
	spi_dev_put(spi_dev);
	return ret;
}

static int gt210_nrf2401_open(struct inode *inode, struct file *filp)
{	dprintk("gt210_nrf2401_open()\n");
	struct gt210_nrf2401_info *info;
	int ret = -ENOENT;
	mutex_lock(&device_list_lock);
	list_for_each_entry(info, &device_list, device_entry) {
		if (info->devt == inode->i_rdev) {
			ret = 0;
			break;
		}
	}
	if (ret == 0) {
		if(info->users == 0){	
#ifdef GT210_NRF2401_TEST
		;
#else 
			ret = gt210_nrf2401_init_op(info);
#endif
			if(ret < 0){
				dev_err(&info->spi_dev->dev, "failed to init nrf2401\n");
				goto exit_1;
			}
		}
		info->users++;
		filp->private_data = info;
		nonseekable_open(inode, filp);
	} else 
		dev_err(&info->spi_dev->dev, "failed to get minor %d\n", iminor(inode));
exit_1:
	mutex_unlock(&device_list_lock);
	dprintk("gt210_nrf2401_open(): end, ret = %d\n", ret);
	return ret;
}
static int gt210_nrf2401_release(struct inode *inode, struct file *filp)
{	dprintk("gt210_nrf2401_release()\n");
	struct gt210_nrf2401_info *info;
	struct gt210_nrf2401_pipe *pipe;
	struct gt210_nrf2401_pbuf *pbuf;
	int ret = 0;
	mutex_lock(&device_list_lock);
	info = filp->private_data;
	filp->private_data = NULL;
	info->users--;
	if (!info->users) {
#ifdef GT210_NRF2401_TEST
		;
#else 
	ret = gt210_nrf2401_deinit_op(info);
#endif
		if(ret < 0){
			dev_err(&info->spi_dev->dev, "failed to deinit nrf2401\n");
			goto exit_1;
		}
		int dofree;
		int i = 0;
		pipe = info->tx_pipe;
		if(pipe != NULL){
			while(! list_empty(&pipe->head)){
				pbuf = list_entry(pipe->head.next, struct gt210_nrf2401_pbuf, list);
				list_del(pipe->head.next);
				gt210_nrf2401_free_pbuf(pbuf);
			}
			kfree(pipe);
			info->tx_pipe = NULL;
		}
		for(i=0; i < NRF2401_ADDR_NUM; i++){
			pipe = info->rx_pipe[i];
			if(pipe != NULL){
				while(! list_empty(&pipe->head)){
					pbuf = list_entry(pipe->head.next, struct gt210_nrf2401_pbuf, list);
					list_del(pipe->head.next);
					gt210_nrf2401_free_pbuf(pbuf);
				}				
				kfree(pipe);
				info->rx_pipe[i] = NULL;
			}
		}
		spin_lock_irq(&info->spi_lock);
		dofree = (info->spi_dev == NULL);
		spin_unlock_irq(&info->spi_lock);
		if (dofree) {
			kfree(info);
		}
	}
exit_1:
	mutex_unlock(&device_list_lock);
	dprintk("gt210_nrf2401_release(): end\n");
	return ret ;
}


/***** interrupt and callback function ****/
static int gt210_nrf2401_tx_callback(struct gt210_nrf2401_pipe *pipe, 
									struct gt210_nrf2401_pbuf *pbuf)
{
	schedule_work(&pipe->work);
}
static int gt210_nrf2401_rx_callback(struct gt210_nrf2401_pipe *pipe, 
									struct gt210_nrf2401_pbuf *pbuf)
{
	spin_lock(&pipe->lock);
	list_add_tail(&pbuf->list, &pipe->head);
	pipe->has_packets++;
	spin_unlock(&pipe->lock);
	schedule_work(&pipe->work);
}
static void gt210_nrf2401_irq_rx(struct gt210_nrf2401_info *info, int addr_num)
{
	struct gt210_nrf2401_pipe *pipe = info->rx_pipe[addr_num];
	struct gt210_nrf2401_pbuf *pbuf;
	int i=0;
	if(pipe == NULL)
		return ;
	pbuf = gt210_nrf2401_alloc_pbuf(NULL);
 	gt210_nrf2401_read_buf(info, READ_REG1 |RD_RX_PLOAD,
		pbuf->pipe_buf, NRF2401_PIPE_LEN);
	//gt210_nrf2401_write_reg(info, 0xe2,0);	
	gt210_nrf2401_recv_buf(pipe);
	
	pbuf->mseconds = jiffies_to_msecs(jiffies);
	pbuf->addr_num = addr_num;
	/*
	dprintk("read  FIFO data: pipe num=%d", addr_num );
	for(i = 0; i<NRF2401_PIPE_LEN; i++){
		if(i%4 == 0) dprintk("\n");
		dprintk("0x%02x  ",pbuf->pipe_buf[i]);
	}
	dprintk("\nread FIFO data: end\n");
	*/
	pipe->packets++;
	pipe->error = 0;
	
	pipe->callback(pipe, pbuf);
	
}
static void gt210_nrf2401_irq_tx(struct gt210_nrf2401_info *info, 
							int addr_num)
{
	struct gt210_nrf2401_pipe *pipe = info->tx_pipe;
	if(pipe == NULL)
		return ;
	pipe->error = 0;
	pipe->packets++;
	//gt210_nrf2401_write_reg(info, 0xe1,0);	
	gt210_nrf2401_set_rx_addr(info->rx_pipe[addr_num]);
	gt210_nrf2401_recv_buf(pipe);
	
	pipe->callback(pipe, pipe->pbuf);
}
static void gt210_nrf2401_irq_tx_error(struct gt210_nrf2401_info *info, 
									int addr_num)
{
	struct gt210_nrf2401_pipe *pipe = info->tx_pipe;
	if(pipe == NULL)
		return ;
	pipe->error = -EIO;
	pipe->err_packets++;
	pipe->packets++;
	//gt210_nrf2401_write_reg(info, 0xe1,0);	
	gt210_nrf2401_set_rx_addr(info->rx_pipe[addr_num]);
	gt210_nrf2401_recv_buf(pipe);
	
	pipe->callback(pipe, pipe->pbuf);
}
static void gt210_nrf2401_irq_work(struct work_struct *work)
{	dprintk("gt210_nrf2401_irq_work()\n");
	struct gt210_nrf2401_info *info =
		container_of(work, struct gt210_nrf2401_info, irq_work);
	unsigned	char status;
	int i=0;
	int addr_num=0;
	gt210_nrf2401_read_status(info, READ_REG1 |STATUS, &status);	
	dev_info(&info->spi_dev->dev,"read status reg: addr=0x%02x, value=0x%02x \n", 
		STATUS, status);
	
	if(status & 0x40){	/***** rx ok *****/
		if((status&0x0e)<=0x0a){					   
	  		addr_num = (status&0x0e) >> 1;
		}else 
			addr_num = 0;
		gt210_nrf2401_irq_rx(info, addr_num);
	}else if((status &0x20)>0){	/***** tx ok *****/
		gt210_nrf2401_irq_tx(info, 0);
	}else if((status &0x10)>0){	/***** tx error *****/	
		gt210_nrf2401_irq_tx_error(info, 0);
	}
	gt210_nrf2401_write_reg(info, WRITE_REG1 | STATUS, status);
	return ;
}
static irqreturn_t gt210_nrf2401_irq(int irq, void *dev_id)
{	//dprintk("gt210_nrf2401_irq()\n");
	struct gt210_nrf2401_info *info = dev_id;
	schedule_work(&info->irq_work);
	return IRQ_HANDLED;
}


/***** probe and remove function ****/
#ifdef GT210_NRF2401_TEST
static void gt210_nrf2401_test_chip(struct gt210_nrf2401_info *info)
{	
	int i = 0;
	int loops = 0;
	int ret = 0;
	
	struct nrf2401_ioc_transfer xfer={
		.ioc_type = NRF2401_IOC_SET_RX_PIPE,
		.addr_num = 0,
	};
	for(i = 0; i<NRF2401_ADDR_LEN; i++){
		xfer.txrx_buf[i] = (u8)0x01;
	}
	ret = gt210_nrf2401_pass_op(info, &xfer);
	if(ret < 0 ){
		dev_err(&info->spi_dev->dev, "failed to set rx addr(%d)\n", xfer.addr_num);
	}else{
		dev_info(&info->spi_dev->dev,
			"good to set rx addr(%d) : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", 
		xfer.addr_num,
		xfer.txrx_buf[0], xfer.txrx_buf[1],
		xfer.txrx_buf[2], xfer.txrx_buf[3],
		xfer.txrx_buf[4]);
	}

	xfer.ioc_type = NRF2401_IOC_SET_TX_PIPE;
	for(i = 0; i<NRF2401_ADDR_LEN; i++){
		xfer.txrx_buf[i] = (u8)0x11;
	}
	ret = gt210_nrf2401_pass_op(info, &xfer);
	if(ret < 0 ){
		dev_err(&info->spi_dev->dev, "failed to set tx addr\n");
	}else{
		dev_info(&info->spi_dev->dev, 
			"good to set tx addr(%d) : 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", 
		xfer.addr_num,
		xfer.txrx_buf[0], xfer.txrx_buf[1],
		xfer.txrx_buf[2], xfer.txrx_buf[3],
		xfer.txrx_buf[4]);
	}	

	xfer.ioc_type = NRF2401_IOC_WRITE_PIPE;
	//xfer.ioc_type = NRF2401_IOC_READ_PIPE;
	for(i = 0; i<NRF2401_PIPE_LEN; i++){
		xfer.txrx_buf[i] = (u8)0x24;
	}
	xfer.addr_num = 0;
	//xfer.rw_mode = NRF2401_IOC_RW_BLOCK;
	xfer.rw_mode = NRF2401_IOC_RW_NOBLOCK;
	loops = 1;
	while(loops--){
		ret = gt210_nrf2401_pass_op(info, &xfer);
		if(ret <0){
			dev_err(&info->spi_dev->dev, "failed to send data\n");
		}else{
			dev_info(&info->spi_dev->dev, "good to send data:");
			for(i = 0; i< NRF2401_PIPE_LEN; i++){
				if(i%4 == 0) 
					dprintk("\n");
				dprintk("0x%02x	",xfer.txrx_buf[i] );
			}
			dprintk("\n");
		}
		msleep(1000);
	}
}
#endif
static int gt210_nrf2401_probe(struct spi_device *spi_dev)
{	dprintk("\n\ngt210_nrf2401_probe(id=%d.%d)\n", spi_dev->master->bus_num, spi_dev->chip_select);
	struct gt210_nrf2401_info *info;
	struct gt210_nrf2401_platdata *pdata;
	unsigned long minor;
	int ret;
	pdata = spi_dev->dev.platform_data;
	if (pdata == NULL) {
		dev_err(&spi_dev->dev,"failed to get platform_data\n");
		return -EINVAL;
	}
	info = kzalloc(sizeof(struct gt210_nrf2401_info), GFP_KERNEL);
	if (info == NULL) {
		dev_err(&spi_dev->dev,"failed to allocate struct gt210_nrf2401_info{}\n");
		return -ENOMEM;
	}
	info->pdata = pdata;
	info->irq = spi_dev->irq;
	info->spi_dev = spi_dev;
	memcpy(info->name, "gt210_nrf2401", sizeof("gt210_nrf2401"));
	INIT_LIST_HEAD(&info->device_entry);
	spin_lock_init(&info->spi_lock);
	mutex_init(&info->buf_lock);
	INIT_WORK(&info->irq_work, gt210_nrf2401_irq_work);
	spi_set_drvdata(spi_dev, info);
	
	mutex_lock(&device_list_lock);
	minor = find_first_zero_bit(minors, N_SPI_MINORS);
	if (minor < N_SPI_MINORS) {
		struct device *dev;
		info->devt = MKDEV(SPICHAR_MAJOR, minor);
		dev = device_create(gt210_nrf2401_class, &spi_dev->dev, info->devt,
				    info, "nrf2401-%d.%d",
				    spi_dev->master->bus_num, spi_dev->chip_select);
		 if(IS_ERR(dev)){
			dev_dbg(&spi_dev->dev, "failed to create device \n");
			ret = IS_ERR(dev) ;
			goto exit_1;
		 }
		 info->dev = dev;
	} else {
		dev_err(&spi_dev->dev, "failed to allocate minor number \n");
		ret = -ENODEV;
		goto exit_1;
	}
	set_bit(minor, minors);
	list_add(&info->device_entry, &device_list);
	mutex_unlock(&device_list_lock);
#ifdef GT210_NRF2401_TEST
	gt210_nrf2401_init_op(info);
	gt210_nrf2401_test_chip(info);
#else
	;
#endif
	
	return 0;
exit_1:
	kfree(info);
	spi_set_drvdata(spi_dev, NULL);
	return ret;
}
static int gt210_nrf2401_remove(struct spi_device *spi_dev)
{	dprintk("gt210_nrf2401_remove(id=%d.%d)\n", 
		spi_dev->master->bus_num, spi_dev->chip_select);
	struct gt210_nrf2401_info *info = spi_get_drvdata(spi_dev);
	if(info == NULL) 
		return 0;
		
#ifdef GT210_NRF2401_TEST
	gt210_nrf2401_deinit_op(info);
#else
	;
#endif
	spin_lock_irq(&info->spi_lock);
	info->spi_dev = NULL;
	spi_set_drvdata(spi_dev, NULL);
	spin_unlock_irq(&info->spi_lock);
	mutex_lock(&device_list_lock);
	list_del(&info->device_entry);
	device_destroy(gt210_nrf2401_class, info->devt);
	 info->dev = NULL;
	clear_bit(MINOR(info->devt), minors);
	if (info->users == 0) 
		kfree(info);
	mutex_unlock(&device_list_lock);
	return 0;
}

static struct spi_driver gt210_nrf2401_spi_driver = {
	.driver = {
		.name =	SPI_DEVICE_NAME,	/* nrf2401 != spidev*/
		.owner =	THIS_MODULE,
	},
	.probe 	= gt210_nrf2401_probe,
	.remove = gt210_nrf2401_remove,
};

/***** init and exit ****/
static int __init gt210_nrf2401_init(void)
{	dprintk("gt210_nrf2401_init()\n");
	int ret;

	BUILD_BUG_ON(N_SPI_MINORS > 256);
	ret = register_chrdev(SPICHAR_MAJOR, SPI_DEVICE_NAME, &gt210_nrf2401_fops);
	if (ret < 0){
		printk(KERN_ERR"failed to register char device\n");
		ret = -EINVAL;
		return ret;
	}
	gt210_nrf2401_class = class_create(THIS_MODULE, SPI_DEVICE_NAME);
	if (IS_ERR(gt210_nrf2401_class)) {
		printk(KERN_ERR"failed to create class\n");
		ret = PTR_ERR(gt210_nrf2401_class);
		goto exit_1;
	}
	ret = spi_register_driver(&gt210_nrf2401_spi_driver);
	if (ret < 0) {
		printk(KERN_ERR"failed to register spi driver\n");
		goto exit_2;
	}
	return 0;

exit_2:
	class_destroy(gt210_nrf2401_class);
	gt210_nrf2401_class = NULL;
exit_1:
	unregister_chrdev(SPICHAR_MAJOR, 
		gt210_nrf2401_spi_driver.driver.name);
	return ret;
}
static void __exit gt210_nrf2401_exit(void)
{	dprintk("gt210_nrf2401_exit()\n");
	spi_unregister_driver(&gt210_nrf2401_spi_driver);
	class_destroy(gt210_nrf2401_class);
	unregister_chrdev(SPICHAR_MAJOR, 
		gt210_nrf2401_spi_driver.driver.name);
}
module_init(gt210_nrf2401_init);
module_exit(gt210_nrf2401_exit);
MODULE_DESCRIPTION("GT2440 NRF2401 SPI Wireless Transceiver Device Driver");
MODULE_AUTHOR("Liguang13579<1659890447@qq.com>");
MODULE_LICENSE("GPL v2");

